package com.fakepath.views;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;

import com.fakepath.listeners.OnScrollBarListener;
import com.fakepath.utils.ViewUtils;

public class ObservableListView extends ListView implements
		OnResetAdapterListener {
	private final int[] coords = new int[2];
	private boolean isScrolling = false;
	private boolean isFling = false;
	private OnScrollBarListener listener;
	private float extentScrollBar;
	private int offsetScrollBar;
	private int rangeScrollBar;
	private int heightTarget;
	private int minHeight;
	private float MIN_DISTANCE = 100;
	private boolean isNeedCal;
	private int yOffsetTouch = 0;

	public ObservableListView(Context paramContext) {
		super(paramContext);
		init();
	}

	public ObservableListView(Context paramContext,
			AttributeSet paramAttributeSet) {
		super(paramContext, paramAttributeSet);
		init();
	}

	public ObservableListView(Context paramContext,
			AttributeSet paramAttributeSet, int paramInt) {
		super(paramContext, paramAttributeSet, paramInt);
		init();
	}

	public void setOnScrollBarChangePosition(OnScrollBarListener listener) {
		this.listener = listener;

		this.setOnScrollListener(new AbsListView.OnScrollListener() {

			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				if (ObservableListView.this.getCount() == 0)
					return;
				if (!checkCanScroll())
					return;
				if (isScrolling) {
					ObservableListView.this.listener.onStartScroll();
					ObservableListView.this.listener
							.onPositionChange(
									computeVerticalScrollOffsetFactor(),
									AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
				} else if (isFling) {
					ObservableListView.this.listener.onFringScroll();
					ObservableListView.this.listener.onPositionChange(
							computeVerticalScrollOffsetFactor(),
							AbsListView.OnScrollListener.SCROLL_STATE_FLING);
				}

			}

			public void onScrollStateChanged(AbsListView paramAbsListView,
					int scrollState) {
				// Kiem tra xem co scroll dc listview khong
				if (!checkCanScroll())
					return;
				if (ObservableListView.this.getCount() == 0)
					return;
				if (scrollState == SCROLL_STATE_FLING) {
					isFling = true;
					isScrolling = false;
				} else if (scrollState == SCROLL_STATE_TOUCH_SCROLL) {
					isScrolling = true;
					isFling = false;
					return;
				} else {
					isScrolling = false;
					isFling = false;
					ObservableListView.this.listener.onStopScroll();
				}
			}
		});
	}

	private boolean checkCanScroll() {
		rangeScrollBar = computeVerticalScrollRange();
		if (rangeScrollBar < ObservableListView.this.getHeight()) {
			isNeedCal = true;
			ObservableListView.this.setOnScrollListener(null);
			ObservableListView.this.setOnTouchListener(onTouchListener);
			return false;
		}
		return true;
	}

	@Override
	protected int computeVerticalScrollExtent() {
		if (isNeedCal) {
			return (int) (getHeight() - MIN_DISTANCE);
		} else {
			return super.computeVerticalScrollExtent();
		}

	}

	@Override
	protected int computeVerticalScrollOffset() {
		if (isNeedCal) {
			return yOffsetTouch;
		} else {
			return super.computeVerticalScrollOffset();
		}
	}

	@Override
	protected int computeVerticalScrollRange() {
		if (isNeedCal) {
			return (int) (getHeight() + MIN_DISTANCE);
		} else {
			return super.computeVerticalScrollRange();
		}
	}

	private void init() {
		heightTarget = ViewUtils.dipToPx(getContext(), 40.0F);
		minHeight = ViewUtils.dipToPx(getContext(), 65.0F);
	}

	private float computeVerticalScrollOffsetFactor() {
		offsetScrollBar = computeVerticalScrollOffset();
		rangeScrollBar = computeVerticalScrollRange();
		extentScrollBar = computeVerticalScrollExtent();

		float heightView = this.getHeight();

		float ratio = rangeScrollBar / heightView;
		float offsetY = 0;

		if (extentScrollBar < minHeight) {
			extentScrollBar = heightView;
			offsetY += extentScrollBar / 2;
		} else {
			float rangeWithTarget = extentScrollBar - (heightTarget * ratio);
			float percentDone = offsetScrollBar
					/ (rangeScrollBar - extentScrollBar);
			offsetY = offsetScrollBar + (rangeWithTarget * percentDone);

		}
		offsetY = offsetY / ratio;
		offsetY += (heightTarget / 2);
		return offsetY;
	}

	public boolean reachedListBottom() {
		if (getChildCount() == 0) {
			return true;
		}
		int j = getLastVisiblePosition();

		int k = getCount();
		int m = getHeight();

		int n = getChildAt(getChildCount() - 1).getBottom();

		if ((j == k - 1) && (n <= m))
			return true;
		else
			return false;
	}

	public boolean reachedListEnds() {
		if ((reachedListTop()) || (reachedListBottom())) {
			return true;
		}
		return false;
	}

	public boolean reachedListTop() {
		if (getChildCount() == 0)
			return true;

		int j = getFirstVisiblePosition();
		int k = getChildAt(0).getTop();
		if ((j == 0) && (k == getPaddingTop())) {
			return true;
		}
		return false;

	}

	public int getItemIndexAtLocation(int position) {
		int i = -1;
		int j = 0;
		int k = 0;
		if (getCount() == 0)
			return i;
		j = getFirstVisiblePosition();
		k = getLastVisiblePosition();
		boolean found = false;
		for (int m = 0; m <= k - j; m++) {
			View localView = getChildAt(m);
			localView.getLocationOnScreen(this.coords);
			if ((position > this.coords[1])
					&& (position <= this.coords[1] + localView.getHeight())) {
				i = m;
				found = true;
				Log.i("ListView", "postion = " + i);
				break;
			}
		}
		if (found)
			i += j;
		else
			i = -1;
		return i;
	}

	private OnTouchListener onTouchListener = new OnTouchListener() {
		private float yPress;
		private float distance;
		private int action;

		@Override
		public boolean onTouch(View v, MotionEvent event) {
			action = event.getAction();
			if (action == MotionEvent.ACTION_DOWN) {
				yPress = event.getY();
			} else if (action == MotionEvent.ACTION_MOVE) {
				distance = event.getY() - yPress;
				yPress = event.getY();

				if (distance < 0)
					distance = Math.abs(distance);
				else
					distance = -distance;

				yOffsetTouch += distance;

				if (yOffsetTouch < 0)
					yOffsetTouch = 0;
				else if (yOffsetTouch + extentScrollBar >= rangeScrollBar) {
					distance = yOffsetTouch + extentScrollBar - rangeScrollBar;
					yOffsetTouch -= distance;
				}

				ObservableListView.this.listener.onStartScroll();
				ObservableListView.this.listener.onPositionChange(
						computeVerticalScrollOffsetFactor(),
						AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
			} else if (action == MotionEvent.ACTION_UP) {
				ObservableListView.this.listener.onStopScroll();
			}

			return false;
		}
	};

	@Override
	public void onNotifiDataSetChange() {
		isNeedCal = false;
		yOffsetTouch = 0;
	}

}